/*
** prestadora.c
** va creando hijos por cada conexión que llega y por cada petición responde OK
** si existe el numero de telefono, y acredita la operación o NRO_INEXISTENTE?
** en caso de que no se pueda realizar la operación.
*/

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <arpa/inet.h>
#include <sys/wait.h>
#include <signal.h>
#include <sqlite3.h>

#define PORTCELULAR "3702"  // the port users will be connecting to

#define BACKLOG 10	 // how many pending connections queue will hold
#define MAXDATASIZE 999



void sigchld_handler(int s)
{
	while(waitpid(-1, NULL, WNOHANG) > 0);
}

// get sockaddr, IPv4 or IPv6:
void *get_in_addr(struct sockaddr *sa)
{
	if (sa->sa_family == AF_INET) {
		return &(((struct sockaddr_in*)sa)->sin_addr);
	}

	return &(((struct sockaddr_in6*)sa)->sin6_addr);
}

void EnteroACadena(char* cadena, int n){
	char base[10]={'0','1','2','3','4','5','6','7','8','9'};
	int nCifras=1,a=10,i;
	while(n/a>0){nCifras++;a*=10;}
	a/=10;
	for(i=0;i<nCifras;i++,a/=10){
		cadena[i]=base[n/a];
		n%=a;	
	}
	cadena[nCifras]='\0';
}


char query[65];
void stringqueryupdate(char num[6], char monto[6]){
	strcat(query,"update prestadora set saldo=");
	strcat(query, monto);
	strcat(query, " where telefono=");
	strcat(query, num);
	strcat(query,";");	
}

void stringqueryselect(char num[6], char monto[6]){
	strcat(query,"select saldo from prestadora where telefono=");
	strcat(query,num);
	strcat(query,";");

}
char resultado[10]={"-1"};
int get_registro(void* paque,int n, char** re,char** nomcol){
	strcpy(resultado, re[0]);
	return 0;
}


int main(void)
{
	int sockfd, new_fd;  // listen on sock_fd, new connection on new_fd
	struct addrinfo hints, *servinfo, *p;
	struct sockaddr_storage their_addr; // connector's address information
	socklen_t sin_size;
	struct sigaction sa;
	int yes=1;
	char s[INET6_ADDRSTRLEN];
	char buf[1000] = {'\0'}; 
	int rv, nuevo_monto=0;
	char luck; // para el random
	char *num, *monto;

	sqlite3* db;
	

	memset(&hints, 0, sizeof hints);
	hints.ai_family = AF_UNSPEC;
	hints.ai_socktype = SOCK_STREAM;
	hints.ai_flags = AI_PASSIVE; // use my IP

	if ((rv = getaddrinfo(NULL, PORTCELULAR, &hints, &servinfo)) != 0) {
		fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(rv));
		return 1;
	}

	// loop through all the results and bind to the first we can
	for(p = servinfo; p != NULL; p = p->ai_next) {
		if ((sockfd = socket(p->ai_family, p->ai_socktype, p->ai_protocol)) == -1) {
			perror("banco: socket");
			continue;
		}

		if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int)) == -1) {
			perror("setsockopt");
			exit(1);
		}

		if (bind(sockfd, p->ai_addr, p->ai_addrlen) == -1) {
			close(sockfd);
			perror("banco: bind");
			continue;
		}

		break;
	}

	if (p == NULL)  {
		fprintf(stderr, "server: failed to bind\n");
		return 2;
	}

	freeaddrinfo(servinfo); // all done with this structure

	if (listen(sockfd, BACKLOG) == -1) {
		perror("listen");
		exit(1);
	}

	sa.sa_handler = sigchld_handler; // reap all dead processes
	sigemptyset(&sa.sa_mask);
	sa.sa_flags = SA_RESTART;
	if (sigaction(SIGCHLD, &sa, NULL) == -1) {
		perror("sigaction");
		exit(1);
	}

	printf("Prestadora: Esperando conexiones...\n");

	while(1) {  // main accept() loop
		sin_size = sizeof their_addr;
		new_fd = accept(sockfd, (struct sockaddr *)&their_addr, &sin_size);
		
		if (new_fd == -1) {
			perror("accept");
			continue;
		}

		inet_ntop(their_addr.ss_family, get_in_addr((struct sockaddr *)&their_addr), s, sizeof(s));
		printf("Prestadora: llego conexion del %s\n", s);

		if (!fork()) { // this is the child process
			close(sockfd); // child doesn't need the listener

			if ((rv = recv(new_fd, buf, MAXDATASIZE-1, 0)) == -1) {
				perror("recv");
				exit(1);
			}
			//se abre la conexion a la BD
			sqlite3_open("bdprestadora.db", &db);
			if((sqlite3_errcode(db))){
				printf("No se pudo conectar a la base de datos\n");
			}else{
				
				num=strtok(buf, " \n\t");
				monto=strtok(NULL, " \n\t");
				stringqueryselect(num, monto);

				//se ejecuta el select para sumar al saldo actual
				//printf("%s\n",query);
				sqlite3_exec(db, query, get_registro, NULL, NULL);
				if (sqlite3_errcode(db)!=0){ 
           				printf("%s\n",sqlite3_errmsg(db));            
        			}else{
					if(atoi(resultado)!=-1){
						nuevo_monto=(atoi(monto)+atoi(resultado));
						EnteroACadena(monto, nuevo_monto);
						stringqueryupdate(num, monto);
						//se ejecuta el update en la BD
						sqlite3_exec(db,query,NULL,NULL,NULL);
						//se ejecuta el UPDATE en la BD
						if (sqlite3_errcode(db)!=0){ 
							printf("%s\n",sqlite3_errmsg(db));            
						}else{
							printf("OK, acreditado. Gs %s al %s\n", monto, num);
							if (send(new_fd, "OK", strlen("OK"), 0) == -1){
								perror("send en el hijo");
							}							
						}	
					}else
					if (send(new_fd, "NRO_INEXISTENTE", strlen("NRO_INEXISTENTE"), 0) == -1)
						perror("send en el hijo");
				}

				
				
			}
			sqlite3_close(db);
			close(new_fd);
			exit(0);
		}
		close(new_fd);  // parent doesn't need this
	}
	return 0;
}

